#include "commandFun.h"
#include "variableDefine.h"
#include "Arduino.h"
#include "print.h"
#include "base64.h"

/////////////////////////////////////////////////////////////////////////////////////////////
// 定义操作pin
extern portcfg pin_list[];
extern int pin_size;

bool pin_on_off(const String& key, bool on) {
    portcfg* pin = nullptr;
    for (auto i = 0; i < pin_size; i++) {
        auto& cfg = pin_list[i];
        if (key == cfg.name) {
            pin = &cfg;
            break;
        }
    }
    if (pin == nullptr) {
        return false;
    }
    if (pin->onoff == on) {
        return true;
    }
    if (on == true) {
        digitalWrite(pin->port, HIGH);
        pin->onoff = true;
    } else {
        digitalWrite(pin->port, LOW);
        pin->onoff = false;
    }
    return true;
}

void pinon(const char* msg, unsigned int length) {
    auto bset = false;
    if (length > 0) {
        bset = pin_on_off(String(msg), true);
    }
    String info = "pin: ";
    info = info + msg + " state: on " + (bset ? "success" : "failed");
    publish(info);
}

void pinoff(const char* msg, unsigned int length) {
    auto bset = false;
    if (length > 0) {
        bset = pin_on_off(String(msg), false);
    }
    String info = "pin: ";
    info = info + msg + " state: off " + (bset ? "success" : "failed");
    publish(info);
}

typedef void(*vfunc)();
void restart(const char* msg, unsigned int length) {
    (*(vfunc)nullptr)();
}

static String g_ip = "";
void setIp(const char* msg, unsigned int length) {
    if (length > 0) {
        g_ip = msg;
        g_ip = g_ip.substring(0, length);
    } else {
        g_ip = "";
    }
    println("g_ip: ", g_ip);
}

static String g_mac = "";
void setMac(const char* msg, unsigned int length) {
    if (length > 0) {
        g_mac = msg;
        g_mac = g_mac.substring(0, length);
    } else {
        g_mac = "";
    }
    println("g_mac: ", g_mac);
}

static bool g_sync = true;
void pinStateReq(const char* msg, unsigned int length) {
    String info = "state";
    info = info + (g_sync ? 1 : 0) + "|[";
    for (auto i = 0; i < pin_size; i++) {
        auto& cfg = pin_list[i];
        info = info + cfg.name + ":" + (cfg.onoff ? 1 : 0) + ",";
    }
    info = info + "]";
    sysPublish(info);
    println("state req, length: ", info.length(), ", info: ", info);
}

void pinStateRsp(const char* data, unsigned int length) {
    println("state rsp, length: ", length, ", data: ", data);
    g_sync = false;
    unsigned int pos = 0, posk = 0, posv = 0;
    char ch = 0, val = 0;
    char key[11] = { 0 };

    while (pos < length) {
        ch = *(data + pos++);
        if (ch == ':') {
            posk = pos;
            if (posk < posv || posk - pos > 10) {
                break;
            }
            strncpy(key, data + posv, posk - posv - 1);
            key[posk - posv] = 0;
        }
        if (ch == ',') {
            if (pos - posk == 2) {
                val = *(data + posk);
                if (val == '0') {
                    pin_on_off(key, false);
                }
                if (val == '1') {
                    pin_on_off(key, true);
                }
            }
            posv = pos;
        }
    }
}

void start(const char* msg, unsigned int length) {
    sysPublish("start");
}

void publish(const String& data) {
    String info = base64_encode(data.c_str(), data.length());
    espPrintln("pub[", info.length(), "]", info);
}

void sysPublish(const String& data) {
    String info = "#" + data;
    publish(info);
}
///////////////////////////////////////////////////////////////////////////////

void deal(String& data) {
    auto lenth = data.length();
    if (lenth <= 0) {
        return;
    }
    auto str = data.c_str();

    auto index = 0;
    while(str[index] == ' ' || str[index] == '\t') {
        index++;
        if(index >= lenth) {
            data = "";
            return;
        }
    }

    str = str + index;
    if (lenth - index > 4) {
        for(auto p = 0; p < lenth - index - 4; p++) {
            auto ptr = str + p;
            if(*ptr == 's' && *(ptr + 1) == 'u' && *(ptr + 2) == 'b' && *(ptr + 3) == '[') {
                str = str + 4;
                auto dlen = 0, i = 0;
                for (; i < lenth; i++) {
                    if (str[i] == ']' && i > 0) {
                        for (auto step = 1, j = 0; j < i; j++) {
                            dlen += (str[i - j - 1] - '0') * step;
                            step *= 10;
                        }
                        break;
                    }
                }

                str = str + i + 1;
                if (dlen > 0 && lenth >= dlen + i + 1) {
                    String subinfo = base64_decode(str, (unsigned int)dlen);
                    if (subinfo.length() > 0) {
                        tdeal(subinfo);
                    }
                }
                data = "";
                return;
            }
        }
    }
    
    if(index > 0) {
        println("receive: index: ", index, ", lenth: ", lenth, ", data: ", data.substring(index));
    } else if (lenth <= 2) {
        if(data[0] != 13) {
            println("receive: data[0]: ", (int)data[0], ", data[1]: ", (int)data[1]);
        }
    } else {
        println("receive: lenth: ", lenth, ", data: ", data);
    }

    data = "";
}

extern command commands[];
extern int command_size;

void tdeal(const String& str) {
    auto length = str.length();
    if (length <= 0) {
        return;
    }
    
    auto data = str.c_str();
    String key;
    unsigned int pos = 0;
    if (*(data + (pos++)) == '[') {
        char ch = 0;
        while (pos < length) {
            ch = *(data + (pos++));
            if (ch != ']') {
                key += ch;
            } else {
                break;
            }
        }
    }
    
    if (pos > length) {
        return;
    }

    if (key.length() <= 0) {
        return;
    }

    for(auto i = 0; i < command_size; i++) {
        auto& cmd = commands[i];
        if (cmd.cmd == nullptr || cmd.length > key.length()) {
            continue;
        }
        if (checkBuffer(key.c_str(), cmd.cmd, key.length(), cmd.length, true) == false) {
            continue;
        }
        cmd.func(data + pos, length - pos);
        return;
    }
}

bool checkBuffer(const char* in, const char* check, int inlen, int cklen, bool equal) {
    if (in == nullptr || check == nullptr) {
        return false;
    }
    if (inlen <= 0) {
        inlen = strlen(in);
    }
    if (cklen <= 0) {
        cklen = strlen(check);
    }
    if (cklen > inlen) {
        return false;
    }
    if (equal == true && cklen != inlen) {
        return false;
    }
    for (int pos = 0; pos < cklen; pos++) {
        if (*(in + pos) != *(check + pos)) {
            return false;
        }
    }
    return true;
}
